package org.quipu.client;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.quipu.client.events.AppEvent;
import org.quipu.client.events.AppEventBus;
import org.quipu.client.events.AppEventListener;
import org.quipu.client.events.CategoriesOutOfDateEvent;
import org.quipu.client.events.CategoryChangedEvent;
import org.quipu.dto.BookDto;
import org.quipu.dto.CategoryDto;

public class CategoryTree extends AppEventWidget implements AppEventListener {

    private final CategoryDto.Type type;
    private final VerticalPanel vPanel;
    private final Tree tree;
    private final FlexTable form;
    private final TextBox editBox;
    private final TextBox createBox;
    private final Button submitBtn;
    private final Button createBtn;
    private final Button removeBtn;
    private CategoryDto selectedCategory;
    private final CategoryDto rootCategory;
    private final Map<TreeItem, CategoryDto> treeHash;

    public CategoryTree(AppEventBus eventBus, AppCache cache, CategoryDto.Type type) {
        super(eventBus, cache);
        this.type = type;
        treeHash = new HashMap<TreeItem, CategoryDto>();
        vPanel = new VerticalPanel();
        editBox = new TextBox();
        editBox.addStyleName("titleInput");
        createBox = new TextBox();
        createBox.addStyleName("titleInput");
        submitBtn = new Button("Сохранить");
        submitBtn.addClickHandler(new ClickHandler() {

            public void onClick(ClickEvent event) {
                updateCategory();
            }
        });
        submitBtn.setEnabled(false);
        createBtn = new Button("Создать");
        createBtn.addClickHandler(new ClickHandler() {

            public void onClick(ClickEvent event) {
                createCategory();
            }
        });
        removeBtn = new Button("Удалить");
        removeBtn.addClickHandler(new ClickHandler() {

            public void onClick(ClickEvent event) {
                removeCategory();
            }
        });
        tree = new Tree();
        tree.addSelectionHandler(new SelectionHandler<TreeItem>() {

            public void onSelection(SelectionEvent<TreeItem> event) {
                selectedCategory = treeHash.get(event.getSelectedItem());
                repaintForm();
            }
        });
        vPanel.add(tree);
        form = new FlexTable();
        vPanel.add(form);

        selectedCategory = null;
        rootCategory = new CategoryDto();
        if(CategoryDto.Type.INCOME.equals(type)) {
            rootCategory.setTitle("Категории доходов");
        } else {
            rootCategory.setTitle("Категории расходов");
        }
    }

    @Override
    public void init() {
//        getEventBus().addListener(BookSelectionChangedEvent.class, this);
//        getEventBus().addListener(LoginEvent.class, this);
        getEventBus().addListener(CategoriesOutOfDateEvent.class, this);
    }

    @Override
    public Widget getWidget() {
        return vPanel;
    }

    private void repaintForm() {
        form.removeAllRows();
        form.setWidget(0, 0, editBox);
        form.setWidget(0, 1, submitBtn);
        form.setWidget(1, 0, createBox);
        form.setWidget(1, 1, createBtn);
        boolean selected = (selectedCategory != null && selectedCategory.getId() != null);
        submitBtn.setEnabled(selected);
        if(selected) {
            form.setWidget(2, 1, removeBtn);
            editBox.setText(selectedCategory.getTitle());
        } else {
            editBox.setText("");
        }
    }

    private void refresh() {
        BookDto book = getCache().getCurrentBook();
        if(book != null) {
            repaintForm();
            repaintTree(tree, getCache().getCategoryTreeCache(type));
        }
    }

    private void repaintTree(Tree tree, List<CategoryDto> categoriesTree) {
        rootCategory.setChilds(categoriesTree);
        editBox.setText("");
        tree.clear();
        TreeItem rootItem = tree.addTextItem(rootCategory.getTitle());
        rootItem.setSelected(selectedCategory == null || selectedCategory.getId() == null);
        treeHash.clear();
        treeHash.put(rootItem, rootCategory);
        populateChilds(rootItem, rootCategory);
        for(TreeItem item: treeHash.keySet()) {
            item.setState(true);
        }
    }

    private void populateChilds(TreeItem parentItem, CategoryDto parentCategory) {
        for(CategoryDto child: parentCategory.getChilds()) {
            TreeItem childItem = parentItem.addTextItem(child.getTitle());
            childItem.setSelected(selectedCategory != null && child.getId().equals(selectedCategory.getId()));
            treeHash.put(childItem, child);
            populateChilds(childItem, child);
        }
    }

    private void createCategory() {
        BookDto book = getCache().getCurrentBook();
        if(book != null) {
            CategoryDto parentCategory = selectedCategory;
            if(parentCategory == null) {
                parentCategory = rootCategory;
            }
            CategoryDto newCategory = new CategoryDto();
            newCategory.setTitle(createBox.getValue());
            newCategory.setBookId(book.getId());
            newCategory.setParentId(parentCategory.getId());
            newCategory.setType(type);
            submitCategory(newCategory);
        }
    }

    private void updateCategory() {
        BookDto book = getCache().getCurrentBook();
        if(selectedCategory != null && selectedCategory.getId() != null && book != null) {
            CategoryDto updatedCategory = new CategoryDto();
            updatedCategory.setId(selectedCategory.getId());
            updatedCategory.setTitle(editBox.getValue());
            updatedCategory.setBookId(book.getId());
            updatedCategory.setParentId(selectedCategory.getParentId());
            updatedCategory.setType(type);
            submitCategory(updatedCategory);
        }
    }

    private void submitCategory(final CategoryDto category) {
        getMainService().submitCategory(category, new AsyncCallback<Void>() {

            public void onSuccess(Void result) {
                getEventBus().fireEvent(new CategoryChangedEvent(CategoryTree.this, category));
            }

            public void onFailure(Throwable caught) {
                Window.alert(caught.getMessage());
            }
        });
    }

    private void removeCategory() {
        getMainService().removeCategory(selectedCategory.getId(), new AsyncCallback<Void>() {

            public void onSuccess(Void result) {
                getEventBus().fireEvent(new CategoryChangedEvent(CategoryTree.this, null));
            }

            public void onFailure(Throwable caught) {
                Window.alert(caught.getMessage());
            }
        });
    }

    @Override
    public void handleEvent(AppEvent event) {
        if(event != null) {
            if(/*event instanceof LoginEvent ||
                event instanceof BookSelectionChangedEvent ||*/
                event instanceof CategoriesOutOfDateEvent) {
                refresh();
            }
        }
    }
}
